//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/compiler/compiler.h"
#include "sagittarius/io/io.h"
#include <string.h>
#include <malloc.h>

void compilerInd(int fd, uint64 ind) {
    uint64 i;
    for(i=0;i<ind;++i)
        SG_WRITE(fd, " ");
}

#if 0
/// create_li
localinfo* create_li() {
    localinfo* li = malloc(sizeof(localinfo));
    li->nm = NULL;
    li->cf = NULL;
    return li;
}

/// release_li
void release_li(localinfo* t) {
    if(t->nm)
        bf_release_name(t->nm);
    free(t);
}

/// dump_localinfo
void dump_localinfo(int fd, localinfo* t, symtab* st, uint64 ind) {
    compilerInd(fd, ind);
    symtabPrintSymK(fd, st, t->s);
    SG_WRITE(2, "\n");
}

/// create_scope
sg_fctx_val* create_scope() {
    sg_fctx_val* z = malloc(sizeof(sg_fctx_val));
    z->n = 0;
    z->c = 4;
    z->l = malloc(z->c*sizeof(localinfo*));
    return z;
}

/// release_scope
void release_scope(sg_fctx_val* z) {
    uint64 i;
    for(i=0;i<z->n;++i) {
        release_li(z->l[i]);
    }
    free(z->l);
    free(z);
}

/// scope_grow
void scope_grow(sg_fctx_val* z) {
    localinfo** l;
    z->c <<= 2;
    l = malloc(z->c*sizeof(localinfo*));
    memcpy(l, z->l, z->n*sizeof(localinfo*));
    free(z->l);
    z->l = l;
}

/// bf_sym_in_fctx
int bf_sym_in_fctx(sg_fctx_val* z, symcode s) {
    uint64 i;
    for(i=0; i<z->n; ++i)
        if(z->l[i]->s == s)
            return 1;
    return 0;
}

/// scope_get_local
localinfo* scope_get_local(sg_fctx_val* z, symcode s) {
    uint64 i;
    for(i=0; i<z->n; ++i)
        if(z->l[i]->s == s)
            return z->l[i];
    return NULL;
}

/// bf_fctx_insert_sym
localinfo* bf_fctx_insert_sym(sg_fctx_val* z, symcode s) {
    localinfo* l;
    l = scope_get_local((z,s);
    if(l)
        return l;
    if(z->n == z->c)
        scope_grow(z);
    l  = create_li();
    l->s = s;
    l->onheap = 0;
    l->isstatic = 1;
    l->isfun = 0;
    z->l[z->n++] = l;
    return l;
}

/// dump_scope
void dump_scope(int fd, sg_fctx_val* z, symtab* st, uint64 ind) {
    uint64 i;
    for(i=0; i<z->n; ++i) {
        compilerInd(fd, ind);
        SG_WRITE(fd, "Local ");
        sg_writeUInt64(fd, i);
        SG_WRITE(fd, ":\n");
        dump_localinfo(fd, z->l[i], st, ind+2);
    }
}

/// dump_local
void dump_local(int fd, localinfo* l, symtab* st) {
    symtabPrintSymK(fd, st, l->s);
    SG_WRITE(fd, " (");
    if(l->onheap)
        SG_WRITE(fd, "heap");
    else
        SG_WRITE(fd, "stack");
    if(l->isstatic)
        SG_WRITE(fd, ", static");
    SG_WRITE(fd, ")");
}
#endif

/// compilerErr
void compilerErr(compiledSrc* c, const char* str, ...) {
    va_list v;
    va_start(v, str);
    vcompilerErr(c, str, v);
    va_end(v);
    fflush(stderr);
}

void vcompilerErr(compiledSrc* c, const char* str, va_list v) {
    vcompilerMsg(c, str, v);
}

void compilerMsg(compiledSrc* c, const char* str, ...) {
    va_list v;
    va_start(v, str);
    vcompilerMsg(c, str, v);
    va_end(v);
    fflush(stderr);
    //AN(0);
}

int cErrMatch(const char** x_, const char a) {
    #define x (*x_)
    AN(*x, "Unexpected end of buffer");
    if(*x == a) {
        ++x;
        return 1;
    } else {
        return 0;
    }
    #undef x
}

void vcompilerMsg(compiledSrc* c, const char* x, va_list v) {
    //vfprintf(stderr, str, v);
    //const char* x = str;
    while(*x) {
        if(*x != '%') {
            fputc(*x, stderr);
            ++x;
        } else {
            ++x;
            AN(x, "Expected code to follow '%'");
            switch(*x) {
                case 'o':
                    ++x;
                    if(cErrMatch(&x,'n') && cErrMatch(&x,'l') && cErrMatch(&x,'i') && cErrMatch(&x,'n') && cErrMatch(&x,'e')) {
                        // %online
                        int64 ln;
                        ln = va_arg(v, symcode);
                        fflush(stderr);
                        if(ln > 0) {
                            SG_WRITE(2, "on line ");
                            sg_writeInt64(2, ln);
                        }
                    } else {
                        AN(0, "Unrecognized pattern");
                    }
                    break;
                case 's':
                    ++x;
                    if(cErrMatch(&x,'y') && cErrMatch(&x,'m')) {
                        // %sym
                        symcode k;
                        k = va_arg(v, symcode);
                        fflush(stderr);
                        if(c->st)
                            symtabPrintSymK(2, c->st, k);
                        else {
                            SG_WRITE(2, "SYMBOL");
                            sg_writeUInt64Hex(2, k);
                        }
                    } else {
                        AN(0, "Unrecognized pattern");
                    }
                    break;
                case 'u':
                    ++x;
                    if(cErrMatch(&x,'l')) {
                        uint64 z;
                        fflush(stderr);
                        z = va_arg(v, uint64);
                        sg_writeUInt64(2, z);
                    } else
                        AN(0, "Want long");
                    break;
                default:
                    AN(0, "Unknown format code");
            }
        }
    }
}

/// releaseCompiledNode
void releaseCompiledNode(compiledNode* x) {
    if(x->a)
        releaseCompiledNode(x->a);
    if(x->b)
        releaseCompiledNode(x->b);
    if(x->z)
        bf_release_fctx(x->z);
    free(x);
}

void check_builtin_fun(compiledSrc* c, compiledNode* x) {
#if 0
    uint64 i;
    for(i=0;i<SG_BUILTIN_TYPE_END;++i) {
        /*SG_WRITE(2, "Builtin rep: 0x");
        sg_writeUInt64Hex(2, c->bc->r[i].s);
        SG_WRITE(2, ", local rep: 0x");
        sg_writeUInt64Hex(2, x->s);
        SG_WRITE(2, "\n");*/
        if(c->bc->r[i].user_vis && c->bc->r[i].s == x->s) {
            //SG_WRITE(2, "Match\n");
            x->st = ST_BUILTIN;
            x->scp.bi = i;
            return;
        }
    }
#endif
    
}

sg_value* resolve_builtin_val(compiledSrc* c, symcode s) {
    if(sg_global_session()->bf->empty_nm->s == s) {
        return (sg_value*)sg_global_session()->bf->empty_nm;
    }
    return NULL;
}
/*
sg_value* check_builtin_val(compiledSrc* c, compiledNode* x) {
    AN(sg_global_session(), "Need session");
    AN(sg_global_session()->bt, "Need session types");
    AN(sg_global_session()->bf, "Need session functions");
    if(sg_global_session()->bf->empty->s == x->s) {
        x->st = ST_BUILTIN_VAL;
        return (sg_value*)sg_global_session()->bf->empty;
    }
    return NULL;
}*/

sg_value* resolve_symbol(compiledSrc* c, sg_fctx_val* z, symcode s) {
    uint64 i;
    AN(sg_global_session(), "Need session");
    AN(sg_global_session()->bt, "Need session types");
    AN(sg_global_session()->bf, "Need session functions");
    for(i=0; i<z->n; ++i) {
        if(z->m[i]->s == s)
            return (sg_value*)z->m[i];
    }
    if(z->par)
        return resolve_symbol(c,z->par,s);
    //else
        return resolve_builtin_val(c, s);
}

/*sg_value* resolve_symbol_val(compiledSrc* c, sg_fctx_val* z, symcode s) {
    uint64 i;
    AN(sg_global_session(), "Need session");
    AN(sg_global_session()->bt, "Need session types");
    AN(sg_global_session()->bf, "Need session functions");
    for(i=0; i<z->n; ++i) {
        if(z->m[i]->s == s)
            return (sg_value*)z->m[i]->o;
    }
    return resolve_builtin_val(c, s);
}*/

void check_local(compiledSrc* c, sg_fctx_val* z, compiledNode* x) {
    uint64 i;
    for(i=0; i<z->n; ++i) {
        if(z->m[i]->s == x->s) {
            x->st = ST_LOCAL;
            //x->scp.li = z->m[i];
            return;
        }
    }
}

/*void determine_sym_type(compiledSrc* c, sg_fctx_val* z, compiledNode* x) {
    bf_dump_fctx(2, z, c->st, 0);
    x->st = ST_INVALID;
    check_builtin_val(c,x);
    check_local(c,z,x);
}*/

/// validateRvalPass1
/*int validateRvalPass1(compiledSrc* c, sg_fctx_val* z, compiledNode* x) {
    switch(x->ptype) {
        case PAST_NODE_TYPE_SYMBOL:
            determine_sym_type(c,z,x);
            if(x->st == ST_INVALID) {
                compilerErr(c, "No such symbol in scope: %sym %online\n", x->s, x->p->ln);
                return 1;
            } else
                return 0;
        case PAST_NODE_TYPE_CALL:
            determine_sym_type(c,z,x);
            if(x->st == ST_INVALID) {
                compilerErr(c, "No such symbol in scope: %sym %online\n", x->s, x->p->ln);
                return 1;
            } else
                return 0;
        case PAST_NODE_TYPE_UINT:
            // it's okie dokie
            return 0;
        default:
            AN(0, "Unhandled node");
    }
}*/

/// validateNodePass1
int validateNodePass1(compiledSrc* c, sg_fctx_val* z, compiledNode* x) {
    AN(x->p);
    
    if(x->p->type == PAST_NODE_TYPE_BBLOCK_LINK) {
        int err=0;
        AN(x->a, "Missing link");
        if(validateNodePass1(c,z,x->a))
            err = 1;
        if(x->b) {
            AT(x->b->ptype == PAST_NODE_TYPE_BBLOCK_LINK, "Expected basic block");
            if(validateNodePass1(c,z,x->b))
                err = 1;
        }
        return err;
    } else if(x->p->type == PAST_NODE_TYPE_CALL) {
        #if 0
        SG_WRITE(2, "validateNodePass1: call\n");
        int defined=1;
        int err=0;
        compiledNode* e;
        compiledFun* f = srcResolveFun(c, z, x->s);
        AN(f, "Can't find function");
        compilerErr(c, "Resolved function %sym\n", x->s);
        uint64 fargs;
        uint64 nargs = getNumCallExpArgs(x->p);
        x->nargs = nargs;
        
        if(!f) {
            compilerErr(c, "No definition of function %sym\n", x->s);
            err = 1;
            defined = 0;
        }
        
        // validate args
        e = x->b;
        while(e) {
            AT(e->ptype == PAST_NODE_TYPE_RVAL_LINK, "Expected rval link");
            AN(e->a, "Empty link");
            if(validateRvalPass1(c, z, e->a))
                err = 1;
            e = e->b;
        }
        
        if(!defined)
            return 1;
        
        // validate # args
        if(f->p->pl)
            fargs = f->p->pl->n;
        else
            fargs = 0;
        
        if(fargs != nargs) {
            compilerErr(c, "Wrong number of parameters for function %sym; defined with %ul but called with %ul\n", x->s, fargs, nargs);
            err = 1;
        }
        
        return err;
        #endif
    } else if(x->p->type == PAST_NODE_TYPE_ASSN) {
        int err=0;
        // left side
        if(x->p->a->type != PAST_NODE_TYPE_FACCESS) {
            //compilerMsg(s, "Assn symbol '%sym'\n", x->a->s);
            bf_fctx_insert_sym(z, x->a->s);
        } else {
//             if(!bf_sym_in_fctx(z, x->a->s)) {
//                 compilerErr(c, "Unknown symbol '%sym' %online\n", x->a->s, x->p->a->ln);
//                 err = 1;
//             }
        }
        
        // right side
        //if(validateRvalPass1(c, z, x->b))
            //err = 1;
        
        return err;
    }
    
    return 0;
}

/// srcResolveSrcFun
/*functionDef* srcResolveSrcFun(compiledSrc* c, sg_fctx_val* z, symcode s) {
    uint64 i;
    sourceFrag* s = c->p;
    AN(s, "No source frag");
    
    for(i=0; i<s->nf; ++i) {
        AN(s->f[i], "Missing fun");
        if(s->f[i]->id == s)
            return s->f[i];
    }
    
    return NULL;
}*/


compiledFun* srcResolveFun(compiledSrc* c, sg_fctx_val* z, symcode s) {
    uint64 i;
    
    // user-defined
    for(i=0; i<c->nf; ++i) {
        AN(c->f[i], "Missing fun");
        if(c->f[i]->s == s)
            return c->f[i];
    }
    
    // builtin
    //return(resolve_builtin_val(c, s));
    return NULL;
}

compileOpt defaultCompOpt() {
    compileOpt opt;
    opt.net_pass = 0;
    return opt;
}

/// compileNode
int compileNode(compiledSrc* c, sg_fctx_val* z, compiledNode* x, compileOpt opt) {
    AN(x->p);
    
    switch(x->ptype) {
        case PAST_NODE_TYPE_BBLOCK_LINK:
            AN(x->a, "Missing link");
            compileNode(c,z,x->a, opt);
            if(x->b) {
                AT(x->b->ptype == PAST_NODE_TYPE_BBLOCK_LINK, "Expected basic block");
                compileNode(c,z,x->b, opt);
            }
            return 0;
        case PAST_NODE_TYPE_ASSN:
            SG_WRITE(2, "Compiling assn\n");
            return compileNode(c, z, x->a, opt) | compileNode(c, z, x->b, opt);
        case PAST_NODE_TYPE_SYMBOL:
            compilerMsg(c, "Compiling symbol '%sym'\n", x->s);
            switch(opt.net_pass) {
                case 0:
                    x->val = resolve_symbol(c,z,x->s);
                    sg_cast_as_name_val(x->val)->sym_tp = SG_SYMTP_NORMAL;
                    break;
                case 1:
                    x->val = (sg_value*)bf_fctx_insert_sym(z, x->s);
                    sg_cast_as_name_val(x->val)->sym_tp = SG_SYMTP_SPECIES;
                    break;
                case 2:
                    x->val = resolve_symbol(c,z,x->s);
                    if(!x->val) {
                        x->val = (sg_value*)bf_fctx_insert_sym(z, x->s);
                        sg_cast_as_name_val(x->val)->sym_tp = SG_SYMTP_RATE_LAW_CONST;
                    }
                    break;
            }
            if(!x->val) {
                compilerErr(c, "Could not resolve symbol %sym\n", x->s);
                AN(0, "Could not resolve symbol");
            }
            if(x->val)
                return 0;
        case PAST_NODE_TYPE_FACCESS:
            SG_WRITE(2, "Compiling faccess\n");
//             x->val = resolve_symbol(c,z,x->s);
//             if(!x->val) {
//                 compilerErr(c, "Could not resolve symbol %sym\n", x->s);
//                 AN(0, "Could not resolve symbol");
//             }
//             if(x->val)
            // compile root node
            compileNode(c, z, x->a, opt);
            return 0;
        case PAST_NODE_TYPE_CALL: {
            SG_WRITE(2, "Compiling call\n");
                //SG_WRITE(2, "Compiling call\n");
//                 compilerMsg(c, "Compiling call for %sym\n", x->s);
//                 uint64 i;
//                 compiledNode *e/*, *p*/;
//                 sg_name_val* f = (sg_name_val*)resolve_symbol(c, z, x->s);
//                 //sg_fun_val* f;
//                 //AT(f_nm->t == (sg_type*)sg_global_session()->bt->name, "Not name-value");
//                 //AT(f_nm->o->t == (sg_type*)sg_global_session()->bt->fun, "Symbol is not callable: wrong type");
//                 //f = (sg_fun_val*)f_nm->o;
//                 if(!f) {
//                     compilerErr(c, "Can't find function %sym, should have been resolved in validation pass\n", x->s);
//                     AN(0, "Can't find function, should have been resolved in validation pass");
//                 }
//                 x->val = (sg_value*)f;
//                 
//                 e = x->b;
//                 i=0;
//                 while(e) {
//                     AT(e->ptype == PAST_NODE_TYPE_RVAL_LINK, "Expected rval link");
//                     AN(e->a, "Empty link");
//                     compileNode(c, z, e->a, opt);
//                     {
//                         /*
//                         paramListElt* o = f->p->pl->p+i;
//                         p = e->a;
//                         //compilerMsg(c, "Processing %sym\n", p->s);
//                         if(o->ref) {
//                             if(p->ptype == PAST_NODE_TYPE_SYMBOL) {
//                                 //SG_WRITE(2, "Is symbol\n");
//                                 // make sure it isn't a builtin
//                                 if(p->st == ST_BUILTIN_VAL) {
//                                     compilerErr(c, "Error(%online): A builtin may not be passed by reference\n", p->p->ln);
//                                     return 1;
//                                 }
//                                 AT(p->st == ST_LOCAL, "Wrong argtype");
//                                 //AN(p->scp.li, "Missing local info");
//                                 //p->scp.li->onheap = 1;
//                                 //p->scp.li->isstatic = 0;
//                             } else {
//                                 //SG_WRITE(2, "Is not a symbol\n");
//                                 compilerErr(c, "Only an lvalue may be passed by reference %online\n", p->p->ln);
//                                 return 1;
//                             }
//                         }*/
//                     }
//                     e = e->b;
//                     ++i;
//                 }
//                 x->nargs = i;
                // compile root (thing being called)
                compileNode(c, z, x->a, opt);
                if(x->b)
                    compileNode(c, z, x->b, opt);
                return 0;
            }
        case PAST_NODE_TYPE_RVAL_LINK: {
            SG_WRITE(2, "Compiling rval link\n");
            AN(x->a, "Empty rval link");
            compileNode(c, z, x->a, opt);
            if(x->b)
                compileNode(c, z, x->b, opt);
            return 0;
            }
        case PAST_NODE_TYPE_IMPORT: {
                uint64 i;
                importStmt* d=x->p->v.imp;
                for(i=0;i<d->n;++i) {
                    d->p[i]->m = bf_fctx_insert_sym(z, d->p[i]->sym);
                }
                return 0;
            }
        case PAST_NODE_TYPE_NETWORK: {
                SG_WRITE(2, "Compiling network\n");
                AN(x->a, "Need reactions");
                AT(x->a->ptype == PAST_NODE_TYPE_RXN_RULE_LINK, "Unexpected type");
                
                // create local scope
                x->z = bf_create_fctx();
                x->z->par = z;
                
                // pass 1: process reactions
                opt.net_pass = 1;
                compileNode(c, x->z, x->a, opt);
                
                // pass 2: process rate laws
                opt.net_pass = 2;
                compileNode(c, x->z, x->a, opt);
                return 0;
            }
        case PAST_NODE_TYPE_RXN_RULE_LINK: {
                SG_WRITE(2, "Compiling rxn rule link\n");
                AN(x->a, "Empty link");
                compileNode(c, z, x->a, opt);
                if(x->b) {
                    AT(x->b->ptype == PAST_NODE_TYPE_RXN_RULE_LINK, "Unexpected type");
                    compileNode(c, z, x->b, opt);
                }
                return 0;
            }
        case PAST_NODE_TYPE_RXN_RULE: {
                SG_WRITE(2, "Compiling rxn rule\n");
                AN(x->a, "Empty reaction");
                if(opt.net_pass == 1)
                    compileNode(c, z, x->a, opt);
                AN(x->b, "Empty rate law");
                if(opt.net_pass == 2)
                    compileNode(c, z, x->b, opt);
                return 0;
            }
        case PAST_NODE_TYPE_RXN: {
                SG_WRITE(2, "Compiling rxn\n");
                AN(x->a, "Empty lhs");
                compileNode(c, z, x->a, opt);
                AN(x->b, "Empty rhs");
                compileNode(c, z, x->b, opt);
                return 0;
            }
        case PAST_NODE_TYPE_ADD: {
                SG_WRITE(2, "Compiling add\n");
                AN(x->a, "Empty lhs");
                compileNode(c, z, x->a, opt);
                AN(x->b, "Empty rhs");
                compileNode(c, z, x->b, opt);
                return 0;
            }
        case PAST_NODE_TYPE_SUB: {
                SG_WRITE(2, "Compiling sub\n");
                AN(x->a, "Empty lhs");
                compileNode(c, z, x->a, opt);
                AN(x->b, "Empty rhs");
                compileNode(c, z, x->b, opt);
                return 0;
            }
        case PAST_NODE_TYPE_MUL: {
                SG_WRITE(2, "Compiling mul\n");
                AN(x->a, "Empty lhs");
                compileNode(c, z, x->a, opt);
                AN(x->b, "Empty rhs");
                compileNode(c, z, x->b, opt);
                return 0;
            }
        case PAST_NODE_TYPE_DIV: {
                SG_WRITE(2, "Compiling div\n");
                AN(x->a, "Empty lhs");
                compileNode(c, z, x->a, opt);
                AN(x->b, "Empty rhs");
                compileNode(c, z, x->b, opt);
                return 0;
            }
        default:
            return 0;
    }
    
    return 0;
}

/// dumpCompiledNode
void dumpCompiledNode(int fd, compiledNode* x, uint64 ind, symtab* st, sg_OpPrec prec) {
    //compilerInd(fd, ind);
    switch(x->ptype) {
        case PAST_NODE_TYPE_SYMBOL:
            dumpParserExp(fd, x->p, ind, st, prec);
            switch(sg_cast_as_name_val(x->val)->sym_tp) {
                case SG_SYMTP_NORMAL:
                    break;
                case SG_SYMTP_SPECIES:
                    SG_WRITE(fd, "[SPECIES]");
                    break;
                case SG_SYMTP_RATE_LAW_CONST:
                    SG_WRITE(fd, "[RL_CONST]");
                    break;
                default:
                    AN(0, "Unknown symbol type");
            }
            return;
        case PAST_NODE_TYPE_ASSN:
            dumpCompiledNode(fd, x->a, ind, st, prec);
            SG_WRITE(fd, " = ");
            dumpCompiledNode(fd, x->b, ind, st, prec);
            SG_WRITE(fd, "");
            return;
        case PAST_NODE_TYPE_BBLOCK_LINK:
            AN(x->a, "Empty link");
            compilerInd(fd, ind);
            dumpCompiledNode(fd, x->a, ind, st, prec);
            SG_WRITE(fd, "\n");
            // next
            if(x->b) {
                AT(x->b->ptype == PAST_NODE_TYPE_BBLOCK_LINK, "Expected basic body link");
                dumpCompiledNode(fd, x->b, ind, st, prec);
            }
            return;
        case PAST_NODE_TYPE_NETWORK: {
            SG_WRITE(fd, "Network:\n");
            compilerInd(fd, ind+2);
            SG_WRITE(fd, "Locals: ");
            bf_dump_fctx(fd, x->z, st, ind+2);
            compilerInd(fd, ind+2);
            dumpCompiledNode(fd, x->a, ind+2, st, prec);
            return;
        }
        case PAST_NODE_TYPE_RXN_RULE_LINK:
            AN(x->a, "Empty link");
            dumpCompiledNode(fd, x->a, ind, st, prec);
            if(x->b) {
                AT(x->b->ptype == PAST_NODE_TYPE_RXN_RULE_LINK, "Expected rule link");
                SG_WRITE(fd, "; ");
                dumpCompiledNode(fd, x->b, ind, st, prec);
            }
            return;
        case PAST_NODE_TYPE_RXN_RULE:
            AN(x->a, "Empty rxn");
            dumpCompiledNode(fd, x->a, ind, st, prec);
            SG_WRITE(fd, ", ");
            AN(x->b, "Empty rate");
            dumpCompiledNode(fd, x->b, ind, st, prec);
            return;
        case PAST_NODE_TYPE_RXN:
            AN(x->a, "Empty lhs");
            dumpCompiledNode(fd, x->a, ind, st, prec);
            SG_WRITE(fd, " → ");
            AN(x->b, "Empty rhs");
            dumpCompiledNode(fd, x->b, ind, st, prec);
            return;
        case PAST_NODE_TYPE_ADD:
            if(SG_OP_ADDSUB < prec)
                SG_WRITE(fd, "(");
            AN(x->a, "Empty lhs");
            dumpCompiledNode(fd, x->a, ind, st, SG_OP_ADDSUB);
            SG_WRITE(fd, " + ");
            AN(x->b, "Empty rhs");
            dumpCompiledNode(fd, x->b, ind, st, SG_OP_ADDSUB);
            if(SG_OP_ADDSUB < prec)
                SG_WRITE(fd, ")");
            return;
        case PAST_NODE_TYPE_SUB:
            if(SG_OP_ADDSUB < prec)
                SG_WRITE(fd, "(");
            AN(x->a, "Empty lhs");
            dumpCompiledNode(fd, x->a, ind, st, SG_OP_ADDSUB);
            SG_WRITE(fd, " - ");
            AN(x->b, "Empty rhs");
            dumpCompiledNode(fd, x->b, ind, st, SG_OP_ADDSUB);
            if(SG_OP_ADDSUB < prec)
                SG_WRITE(fd, ")");
            return;
        case PAST_NODE_TYPE_MUL:
            if(SG_OP_DIVMUL < prec)
                SG_WRITE(fd, "(");
            AN(x->a, "Empty lhs");
            dumpCompiledNode(fd, x->a, ind, st, SG_OP_DIVMUL);
            SG_WRITE(fd, "*");
            AN(x->b, "Empty rhs");
            dumpCompiledNode(fd, x->b, ind, st, SG_OP_DIVMUL);
            if(SG_OP_DIVMUL < prec)
                SG_WRITE(fd, ")");
            return;
        case PAST_NODE_TYPE_DIV:
            if(SG_OP_DIVMUL < prec)
                SG_WRITE(fd, "(");
            AN(x->a, "Empty lhs");
            dumpCompiledNode(fd, x->a, ind, st, SG_OP_DIVMUL);
            SG_WRITE(fd, "/");
            AN(x->b, "Empty rhs");
            dumpCompiledNode(fd, x->b, ind, st, SG_OP_DIVMUL);
            if(SG_OP_DIVMUL < prec)
                SG_WRITE(fd, ")");
            return;
        default:
            dumpParserExp(fd, x->p, ind, st, prec);
    }
}

#if 0
void compiledFunAppArg(compiledFun* f, typecode t) {
    if(f->t) {
        typecode* tt=malloc((f->nt+1)*sizeof(typecode));
        tt[f->nt++] = t;
        free(f->t);
        f->t = tt;
    } else {
        f->t = malloc(sizeof(typecode));
        f->t[0] = t;
    }
}

/// createCompiledFun
compiledFun* createCompiledFun(functionDef* p, typecode t_, ...) {
    va_list v;
    compiledFun* f;
    
    va_start(v, t_);
    f = vcreateCompiledFun(p, t_, v);
    va_end(v);
    
    return f;
}

/// vcreateCompiledFun
compiledFun* vcreateCompiledFun(functionDef* p, typecode t_, va_list v) {
    typecode t;
    compiledFun* f = malloc(sizeof(compiledFun));
    
    f->t = NULL;
    f->nt = 0;
    t = t_;
    
    while(t != -1) {
        compiledFunAppArg(f, t);
        t = va_arg(v, typecode);
    }
    
    return f;
}
#endif

/// releaseCompiledFun
void releaseCompiledFun(compiledFun* f) {
    bf_release_fctx(f->ctx);
    bf_release_fun(f->fv);
    free(f);
}

/// dumpCompiledFun
void dumpCompiledFun(int fd, compiledFun* f, uint64 ind, symtab* st) {
    compilerInd(fd, ind);
    symtabPrintSymK(fd, st, f->p->id);
    SG_WRITE(fd, "\n");
    
    compilerInd(fd, ind+2);
    SG_WRITE(fd, "Locals: ");
    bf_dump_fctx(fd, f->ctx, st, ind+2);
    
    if(f->p->pl) {
        dumpParamList(fd, f->p->pl, ind+2, st);
    }
    if(f->b) {
        compilerInd(fd, ind);
        SG_WRITE(fd, "Body:\n");
        dumpCompiledNode(fd, f->b, ind+2, st, sg_getOpPrec(f->b->ptype));
    }
}

/// create_compiled_node
compiledNode* create_compiled_node(parserExp* p) {
    compiledNode* x = malloc(sizeof(compiledNode));
    x->p = p;
    x->ptype = p->type;
    x->s = p->lvalid;
    if(p->a)
        x->a = create_compiled_node(p->a);
    else
        x->a = NULL;
    if(p->b)
        x->b = create_compiled_node(p->b);
    else
        x->b = NULL;
    
    // locals
    x->z = NULL;
    
    return x;
}

/// constructCompiler
compiledSrc* constructCompiler(sourceFrag* s) {
    uint64 i;
    compiledSrc* c = malloc(sizeof(compiledSrc));
    c->p = s;
    
    // sym table
    c->st = NULL;
    
    // global scope
    c->g = bf_create_fctx();
    
    if(s->f) {
        c->nf = s->nf;
        c->f = malloc(c->nf*sizeof(compiledFun*));
        for(i=0; i<c->nf; ++i) {
            c->f[i] = malloc(sizeof(compiledFun));
            c->f[i]->p = s->f[i];
            c->f[i]->s = c->f[i]->p->id;
            // create the function's context
            c->f[i]->ctx = bf_create_fctx();
            // set the parent context of the function to be global
            c->f[i]->ctx->par = c->g;
            c->f[i]->b = create_compiled_node(c->f[i]->p->b);
        }
    } else {
        c->nf = 0;
        c->f = NULL;
    }
    
    c->x = create_compiled_node(s->b);
    return c;
}

/// releaseCompiler
void releaseCompiler(compiledSrc* c) {
    uint64 i;
    for(i=0; i<c->nf; ++i)
        releaseCompiledFun(c->f[i]);
    free(c->f);
    if(c->x)
        releaseCompiledNode(c->x);
    
    bf_release_fctx(c->g);
    
    free(c);
}

/// dumpCompiler
void dumpCompiler(int fd, compiledSrc* c, uint64 ind, symtab* st) {
    uint64 i;
    compilerInd(fd, ind);
    SG_WRITE(fd, "Compiled source:\n");
    compilerInd(fd, ind+2);
    SG_WRITE(fd, "Functions: ");
    sg_writeUInt64(fd, c->nf);
    SG_WRITE(fd, "\n");
    for(i=0; i<c->nf; ++i) {
        dumpCompiledFun(fd, c->f[i], ind+4, st);
    }
    
    // context
    compilerInd(fd, ind+2);
    SG_WRITE(fd, "Locals: ");
    bf_dump_fctx(fd, c->g, st, ind+2);
    
    if(c->x) {
        compilerInd(fd, ind+2);
        SG_WRITE(fd, "Body:\n");
        dumpCompiledNode(fd, c->x, ind+4, st, sg_getOpPrec(c->x->ptype));
    }
}

int compileFirstPass(compiledSrc* s) {
    sg_name_val* m;
    compiledFun* f;
    //sg_name_val* m;
    int err=0;
    uint64 i, j;
    
    // functions
    for(i=0; i<s->nf; ++i) {
        f = s->f[i];
        f->fv = bf_create_fun();
        f->fv->s = f->s;
        f->fv->nargs = f->p->pl->n;
        compilerMsg(s, "Compile first pass: function %sym, nargs = %ul\n", f->s, f->fv->nargs);
        f->fv->vd = 1;
        // local
        //m = bf_fctx_insert_sym(s->g, f->s);
        // insert function name into globals
        m = bf_fctx_insert_sym(s->g, f->s);
        f->m = m; // TODO: inc ref for name val
        // make the value point to the function obj
        m->o = (sg_value*)f->fv;
        //t->cf = s->f[i];
        
        // params
        for(j=0; j<f->p->pl->n; ++j) {
            //sg_name_val* pm;
            bf_fctx_insert_sym(f->ctx, f->p->pl->p[j].sym.k);
           
        }
        
        // body
        if(validateNodePass1(s, f->ctx, f->b))
            err = 1;
        compileNode(s, f->ctx, f->b, defaultCompOpt());
    }
    
    // nodes
    if(s->x) {
        SG_WRITE(2, "Compiling main body\n");
        if(validateNodePass1(s, s->g, s->x))
            err = 1;
        if(compileNode(s, s->g, s->x, defaultCompOpt()))
            err = 1;
    }
    
    return err;
}
